package com.web.kdl.util;

import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.*;
import com.serotonin.modbus4j.sero.util.queue.ByteQueue;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * modbus通讯工具类,采用modbus4j实现
 *
 * @author slb
 * @dependencies modbus4j-3.0.3.jar
 * @website https://github.com/infiniteautomation/modbus4j
 */
@Slf4j
@Component
public class Modbus4jUtils {

    /**
     * 读取[01 Coil Status 0x]类型 开关数据
     *
     * @param slaveId
     *            slaveId
     * @param offset
     *            位置
     * @return 读取值
     * @throws ModbusTransportException
     *             异常
     * @throws ErrorResponseException
     *             异常
     * @throws ModbusInitException
     *             异常
     */
    public static Boolean readCoilStatus(ModbusMaster master,int slaveId, int offset)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 01 Coil Status
        BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);
        Boolean value = master.getValue(loc);
        return value;
    }

    /**
     * 读取[02 Input Status 1x]类型 开关数据
     *
     * @param slaveId
     * @param offset
     * @return
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     * @throws ModbusInitException
     */
    public static Boolean readInputStatus(ModbusMaster master,int slaveId, int offset)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 02 Input Status
        BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, offset);
        Boolean value = master.getValue(loc);
        return value;
    }

    /**
     * 读取[03 Holding Register类型 2x]模拟量数据
     *
     * @param slaveId
     *            slave Id
     * @param offset
     *            位置
     * @param dataType
     *            数据类型,来自com.serotonin.modbus4j.code.DataType
     * @return
     * @throws ModbusTransportException
     *             异常
     * @throws ErrorResponseException
     *             异常
     * @throws ModbusInitException
     *             异常
     */
    public static Number readHoldingRegister(ModbusMaster master,int slaveId, int offset, int dataType)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 03 Holding Register类型数据读取
        BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);
        Number value = master.getValue(loc);
        return value;
    }

    /**
     * 读取[04 Input Registers 3x]类型 模拟量数据
     *
     * @param slaveId
     *            slaveId
     * @param offset
     *            位置
     * @param dataType
     *            数据类型,来自com.serotonin.modbus4j.code.DataType
     * @return 返回结果
     * @throws ModbusTransportException
     *             异常
     * @throws ErrorResponseException
     *             异常
     * @throws ModbusInitException
     *             异常
     */
    public static Number readInputRegisters(ModbusMaster master,int slaveId, int offset, int dataType)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 04 Input Registers类型数据读取
        BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);
        Number value = master.getValue(loc);
        return value;
    }

    public Object executeHexCommand(ModbusMaster master,String hexCommand) {
        try {
            // 解析十六进制指令
            ModbusRequest request = parseHexCommand(hexCommand);
            return executeModbusRequest(master,request);
        } catch (Exception e) {
            log.error("执行Modbus指令失败: {}", hexCommand, e);
            throw new RuntimeException("Modbus通信失败", e);
        }
    }

    private ModbusRequest parseHexCommand(String hexCommand) throws ModbusTransportException {
        // 移除空格并转换为字节数组
        String cleanHex = hexCommand.replaceAll("\\s+", "");
        byte[] bytes = hexStringToByteArray(cleanHex);

        // 根据您的指令 "06 E4 00 00 00 06 01 03 9C 41 00 64" 解析
        // 假设这是读取保持寄存器的请求

        int slaveId = bytes[6] & 0xFF;  // 从站地址: 0x01
        int functionCode = bytes[7] & 0xFF; // 功能码: 0x03 (读保持寄存器)
        int startAddress = ((bytes[8] & 0xFF) << 8) | (bytes[9] & 0xFF); // 起始地址: 0x9C41
        int quantity = ((bytes[10] & 0xFF) << 8) | (bytes[11] & 0xFF); // 数量: 0x0064

        log.info("解析结果 - 从站ID: {}, 功能码: {}, 起始地址: {}, 数量: {}",
                slaveId, functionCode, startAddress, quantity);

        // 创建Modbus请求
        switch (functionCode) {
            case 0x03: // 读保持寄存器
                return new ReadHoldingRegistersRequest(slaveId, startAddress, quantity);
            case 0x04: // 读输入寄存器
                return new ReadInputRegistersRequest(slaveId, startAddress, quantity);
            case 0x01: // 读线圈
                return new ReadCoilsRequest(slaveId, startAddress, quantity);
            case 0x02: // 读离散输入
                return new ReadDiscreteInputsRequest(slaveId, startAddress, quantity);
            case 0x05: // 写单个线圈
                return new WriteCoilRequest(slaveId, startAddress, true);
            case 0x06: // 写单个寄存器
                int value = ((bytes[10] & 0xFF) << 8) | (bytes[11] & 0xFF);
                return new WriteRegisterRequest(slaveId, startAddress, value);
            default:
                throw new IllegalArgumentException("不支持的功能码: " + functionCode);
        }
    }
    /**
     * 执行Modbus请求
     */
    private Object executeModbusRequest(ModbusMaster master,ModbusRequest request) throws ModbusTransportException {
        ModbusResponse response = (ModbusResponse) master.send(request);

        if (response.isException()) {
            throw new ModbusTransportException("Modbus异常响应: " + response.getExceptionMessage());
        }

        // 根据请求类型处理响应
        if (request instanceof ReadHoldingRegistersRequest) {
            ReadHoldingRegistersResponse regResponse = (ReadHoldingRegistersResponse) response;
            return processHoldingRegistersResponse(regResponse);
        } else if (request instanceof ReadInputRegistersRequest) {
            ReadInputRegistersResponse regResponse = (ReadInputRegistersResponse) response;
            return processInputRegistersResponse(regResponse);
        } else if (request instanceof ReadCoilsRequest) {
            ReadCoilsResponse coilResponse = (ReadCoilsResponse) response;
            return processCoilsResponse(coilResponse);
        } else if (request instanceof ReadDiscreteInputsRequest) {
            ReadDiscreteInputsResponse discreteResponse = (ReadDiscreteInputsResponse) response;
            return processDiscreteInputsResponse(discreteResponse);
        } else if (request instanceof WriteCoilRequest) {
            WriteCoilResponse writeResponse = (WriteCoilResponse) response;
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("address", writeResponse.getWriteOffset());
            return result;
        } else if (request instanceof WriteRegisterRequest) {
            WriteRegisterResponse writeResponse = (WriteRegisterResponse) response;
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("address", writeResponse.getWriteOffset());
            result.put("value", writeResponse.getWriteValue());
            return result;
        }

        return null;
    }
    /**
     * 处理保持寄存器响应
     */
    private List<Object> processHoldingRegistersResponse(ReadHoldingRegistersResponse response) {
        return processRegisterData(response.getShortData());
    }

    /**
     * 处理输入寄存器响应
     */
    private List<Object> processInputRegistersResponse(ReadInputRegistersResponse response) {
        return processRegisterData(response.getShortData());
    }
    /**
     * 处理寄存器数据
     */
    private List<Object> processRegisterData(short[] registerData) {
        List<Object> result = new ArrayList<>();

        for (int i = 0; i < registerData.length; i++) {
            Map<String, Object> register = new HashMap<>();
            register.put("index", i);
            register.put("rawValue", registerData[i]);
            register.put("hexValue", String.format("0x%04X", registerData[i] & 0xFFFF));
            register.put("intValue", registerData[i] & 0xFFFF);
            register.put("signedIntValue", (int) registerData[i]);

//            // 如果可能的话，尝试解释为浮点数
//            if (i + 1 < registerData.length) {
//                float floatValue = convertToFloat(registerData[i], registerData[i + 1]);
//                register.put("floatValue", floatValue);
//            }

            result.add(register);
        }

        return result;
    }
    /**
     * 处理线圈响应
     */
    private List<Object> processCoilsResponse(ReadCoilsResponse response) {
        boolean[] coilData = response.getBooleanData();
        List<Object> result = new ArrayList<>();

        for (int i = 0; i < coilData.length; i++) {
            Map<String, Object> coil = new HashMap<>();
            coil.put("index", i);
            coil.put("value", coilData[i]);
            result.add(coil);
        }

        return result;
    }

    /**
     * 处理离散输入响应
     */
    private List<Object> processDiscreteInputsResponse(ReadDiscreteInputsResponse response) {
        boolean[] discreteData = response.getBooleanData();
        List<Object> result = new ArrayList<>();

        for (int i = 0; i < discreteData.length; i++) {
            Map<String, Object> discrete = new HashMap<>();
            discrete.put("index", i);
            discrete.put("value", discreteData[i]);
            result.add(discrete);
        }

        return result;
    }
    private byte[] hexStringToByteArray(String hex) {
        int len = hex.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
                    + Character.digit(hex.charAt(i + 1), 16));
        }
        return data;
    }

    // 以下内容为 直接采用报文格式进行 数据传输
    /**
     * 执行Modbus请求并返回完整的原始字节数组（包含请求和响应）
     */
    public byte[] executeAndGetCompleteRawBytes(ModbusMaster master, String hexCommand) {
        try {
            ModbusRequest request = parseHexCommand(hexCommand);
            return executeAndGetCompleteRawResponse(master, request);
        } catch (Exception e) {
            log.error("执行Modbus指令失败: {}", hexCommand, e);
            throw new RuntimeException("Modbus通信失败", e);
        }
    }

    /**
     * 执行请求并获取完整的原始响应字节（包含请求和响应）
     */
    private byte[] executeAndGetCompleteRawResponse(ModbusMaster master, ModbusRequest request) throws ModbusTransportException {
        // 首先获取请求的原始字节
        byte[] requestBytes = getRequestRawData(request);

        // 执行请求获取响应
        ModbusResponse response = master.send(request);

        if (response.isException()) {
            throw new ModbusTransportException("Modbus异常响应: " + response.getExceptionMessage());
        }

        // 获取响应的原始字节
        byte[] responseBytes = getResponseRawData(response);

        // 合并请求和响应字节数组
        byte[] completeBytes = new byte[requestBytes.length + responseBytes.length];
        System.arraycopy(requestBytes, 0, completeBytes, 0, requestBytes.length);
        System.arraycopy(responseBytes, 0, completeBytes, requestBytes.length, responseBytes.length);

        return completeBytes;
    }

    /**
     * 获取请求的原始字节数据
     */
    private byte[] getRequestRawData(ModbusRequest request) {
        ByteQueue queue = new ByteQueue();
        try {
            // 写入Modbus请求头等信息
            request.write(queue);
            return queue.popAll();
        } catch (Exception e) {
            log.error("获取原始请求数据失败", e);
            return new byte[0];
        }
    }

    /**
     * 获取响应的原始字节数据
     */
    private byte[] getResponseRawData(ModbusResponse response) {
        ByteQueue queue = new ByteQueue();
        try {
            // 写入Modbus响应头等信息
            response.write(queue);
            return queue.popAll();
        } catch (Exception e) {
            log.error("获取原始响应数据失败", e);
            return new byte[0];
        }
    }

    /**
     * 获取完整的十六进制响应（包含请求和响应）
     */
    public String getCompleteHexResponse(ModbusMaster master, String hexCommand) {
        try {
            byte[] completeBytes = executeAndGetCompleteRawBytes(master, hexCommand);

            // 构建十六进制字符串
            StringBuilder sb = new StringBuilder();
            for (byte b : completeBytes) {
                sb.append(String.format("%02X ", b));
            }

            return sb.toString().trim();

        } catch (Exception e) {
            log.error("获取完整Modbus响应失败: {}", hexCommand, e);
            return null;
        }
    }


      /**
         * 测试
         *
         * @param args
         */
    public static void main(String[] args) {
        try {
            // 03测试
            IpParameters params = new IpParameters();
            params.setHost("127.0.0.1");
            params.setPort(502);
            ModbusFactory modbusFactory = new ModbusFactory();
            ModbusMaster tcpMaster = modbusFactory.createTcpMaster(
                    params, false
            );
            //00 1C 00 00 00 06 00 03 9F 61 00 62
            String hexResponseOnly = new Modbus4jUtils().getCompleteHexResponse(tcpMaster, "00 09 00 00 00 06 01 03 9F 61 00 62");
//            byte[] bytes = new Modbus4jUtils().executeAndGetRawBytes(tcpMaster, "06E40000000601039C410064");
            System.out.println(hexResponseOnly);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}